using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Reflection;
using Needle.Core.Factory;
using Needle.Core.Injection;
using Needle.Core.Lifetime;
using Needle.Utils;

namespace Needle.Core.Activation
{
    /// <summary>
    /// This activator gets the information to construct the type to activate from reflection.
    /// </summary>
    [DebuggerDisplay("ReflectionActivator {Id}")]
    public class ReflectionActivator : ActivatorBase
    {

        #region constructor

        private IFactory _Factory;
        /// <summary>
        /// Initializes a new instance 
        /// </summary>
        /// <param name="id">The dependency this activator is responsible for.</param>
        /// <param name="toActivate">the concrete type to activate</param>
        public ReflectionActivator(InstanceId id, Type toActivate)
            : base(id, new ReflectionActivationInfo(id, toActivate))
        {
            _Factory = new DynamicMethodFactory(toActivate, (ConstructorInfo)Info.Constructor.Method);
        }

        #endregion

        #region IActivator Members

        /// <summary>
        /// Constructs a new instance.
        /// </summary>
        /// <returns></returns>
        public override object CreateInstance()
        {
            Ensure.NotDisposed(this);

            List<object> arguments = new List<object>();
            IActivationContext currentContext = Manager.CurrentContext;
            foreach (IParameter parameter in Info.Constructor.Parameters)
            {
                arguments.Add(currentContext.GetInstance(parameter.Dependency));
            }
            object instance = _Factory.CreateInstance(arguments.ToArray());
            return instance;
        }

        #endregion

        /// <summary>
        /// Injects properies and methods and registers this instance according
        /// to the lifetime tracking policy.
        /// </summary>
        public override void ConfigureInstance(object instance)
        {
            IActivationContext currentContext = Manager.CurrentContext;
            foreach (IInjectionSite site in Info.Methods)
                Inject(instance, site, currentContext);
            ITrackingPolicy policy = TrackingPolicyAttribute.GetFor(this.Info.Type);
            if (policy != null)
                currentContext.Register(Id, instance, policy);
        }

        private void Inject(object instance, IInjectionSite site, IActivationContext currentContext)
        {
            if (site.Parameters.Count == 0)
            {
                site.Method.Invoke(instance, null);
            }
            else
            {
                List<object> arguments = new List<object>();
                foreach (IParameter parameter in site.Parameters)
                {
                    object argument = currentContext.GetInstance(parameter.Dependency);
                    arguments.Add(argument);
                }
                site.Method.Invoke(instance, arguments.ToArray());
            }
        }
    }

}

